<template>
  <div class="main">
    <div class="ops">
      <el-button type="primary" @click="addServer">添加新服务器</el-button>
    </div>
    <div class="servers" ref="servers">
      <canvas id="canvas" ref="canvas"></canvas>
    </div>
    <!-- 添加服务器的弹出框（填写服务器的信息表格） -->
    <el-dialog title="添加服务器" :visible.sync="dialogVisible" width="30%">
      <div class="form">
        <p>服务器名称：
          <el-input placeholder="请输入服务器名称" v-model="inputServer.serverName"></el-input>
        </p>
        <p>服务器IP：
          <el-input placeholder="请输入服务器的IP" v-model="inputServer.serverIp"></el-input>
        </p>
        <p>服务器端口：
          <el-input placeholder="请输入服务器的端口" v-model="inputServer.serverPort"></el-input>
        </p>
        <p>服务器用户名：
          <el-input placeholder="请输入服务器的端口" v-model="inputServer.username"></el-input>
        </p>
        <p>服务器密码：
          <el-input placeholder="请输入服务器的密码" v-model="inputServer.pass"></el-input>
        </p>
      </div>
      <div slot="footer" class="dialog-footer">
        <p style="text-align:left;margin-bottom:15px;">
          <span
            style="color:#666;font-size:12px;border:1px solid #ccc;border-radius:10%;padding:5px;cursor:pointer;"
            @click="testConn"
          >测试数据库连接</span>
        </p>
        <p style="text-align:center;">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="addServerToBack">确 定</el-button>
        </p>
      </div>
    </el-dialog>
    <!-- 右键弹出的菜单项 -->
    <div class="menu" v-show="menuVisible" ref="menu">
      <ul>
        <li @click="deleteServer">删除该节点</li>
        <!-- <li>重置主服务器</li> -->
        <li @click="stopSlave">关闭slave</li>
        <li @click="startSlave">开启slave</li>
      </ul>
    </div>
  </div>
</template>
<script>
import Api from "../api.js";
import constant from "../static/constant.js";
import api from "../api.js";
const ONLINE_STATUS = 1;
const OFFLINE_STATUS = 0;
const MASTER = 0;
const SLAVE = 1;
export default {
  created() {
    // 创建时加载所有的已经配置的数据库服务器
    this.getServers();
  },
  mounted() {
    // 当vm挂载后，绘制关系图
    let canvas = this.$refs.canvas;
    canvas.width = this.$refs.servers.clientWidth;
    canvas.height = this.$refs.servers.clientHeight;
    canvas.addEventListener('click',(e)=>{
      this.menuVisible = false;
    })
    setInterval(() => {
      this.getServers();
    }, this.requestInterval);
  },
  data() {
    return {
      servers: [],
      serversTree: [],
      requestInterval: 10000,
      nodes: [],
      nodeWidth: 100,
      nodeHeight: 100,
      dialogVisible: false,
      inputServer: {
        serverName: "",
        serverIp: "",
        serverPort: 3306,
        username: "",
        pass: ""
      },
      menuVisible:false,
      selectedServer:{},
      masterConfigDialogVis: true
    };
  },
  methods: {
    // 从后台获取所有数据库服务器信息，并将它们按照层级关系组成serverTree
    getServers() {
      this.$axios
        .post(Api.allServer)
        .then(result => {
          if (result.status) {
            let serversMap = {};
            this.servers = []
            this.serversTree = []
            for (let i = 0; i < result.list.length; i++) {
              this.$set(this.servers, i, result.list[i]);
              this.servers[i].slaves = new Array();
              let serverId = this.servers[i].id;
              serversMap[serverId] = this.servers[i];
            }
            this.servers.forEach(e => {
              if (!serversMap[e.masterServerId]) {
                // 该节点作为主节点，并且没有主节点,将他放入到this.serversTree
                this.serversTree.push(e);
              } else {
                // 该节点作为主节点/从节点，并且他还是另一个server的从节点,将他放入到他的master的slaves数组中
                let masterId = e.masterServerId;
                serversMap[masterId].slaves.push(e);
              }
            });
            // 节点不够，再增加新节点放入nodes中
            if (this.servers.length > this.nodes.length) {
              let count = this.servers.length - this.nodes.length;
              for (let i = 0; i < count; i++) {
                let node = new JTopo.Node();
                node.setSize(this.nodeWidth, this.nodeHeight);
                node.textPosition = "Middle_Center"; // 文字居中
                node.font = "14px 微软雅黑"; // 字体
                node.fontColor = "255,255,255";
                this.nodes.push(node);
              }
            }
            this.paintServers();
          } else {
            this.$message.error(result.msg);
          }
        })
        .catch(err => {
          this.$message.error(err.message);
        });
    },
    // 绘制网络图
    paintServers() {
      // 移除所有node的监听事件
      this.nodes.forEach(n => {
        n.removeAllEventListener;
      });
      // 先移除画布上所有的node和线条
      this.scene.clear();
      // 创建一个“map”，key：server的id，value：jTopo.Node
      let serverNodes = {};
      for (let i = 0; i < this.servers.length; i++) {
        let server = this.servers[i];
        let node = this.nodes[i];
        node.serverId = server.id;
        node.server = server;
        // 给node添加点击事件，点击node时将node的详细信息展示
        node.click(e => {
          let targetServer = e.target.server;
          let content = `
            <p>服务器ID：${targetServer.id}</p>
            <p>服务器名称：${targetServer.serverName}</p>
            <p>服务器ip：${targetServer.serverIp}</p>
            <p>服务器port：${targetServer.serverPort}</p>
            <p>服务器状态：${
              targetServer.serverStatus == ONLINE_STATUS ? "上线" : "宕机"
            }</p>`;
          content += `<p>服务器的master_status:</p>`;
          content += '<ul style="margin-left:20px;">';
          for (let item in targetServer.dbMasterStatus) {
            content += `<li>${item} : ${
              targetServer.dbMasterStatus[item]
            }</li>`;
          }
          content += "</ul>";
          this.$alert(content, `${server.serverName} 的详细信息`, {
            dangerouslyUseHTMLString: true,
            confirmButtonText: "确定",
            callback: action => {}
          });
        });
        node.addEventListener("mouseup", e => {
          if (!e) e = window.event;
          if (e.button == 2) {
            this.menuVisible = true;
            this.selectedServer = e.target.server
            this.$refs.menu.style.left = e.clientX + 'px';
            this.$refs.menu.style.top = e.clientY + 'px';
          }
        });
        // node.add
        node.text = server.serverName;
        // 如果服务是OFFLINE状态设置填充颜色为灰色,如果是ONLINE状态设置为绿色
        if (server.serverStatus == ONLINE_STATUS) {
          node.fillColor = "64,158,255";
        } else {
          node.fillColor = "160,160,160";
        }
        // this.scene.add(node)
        serverNodes[server.id] = node;
      }

      // 根据serverTree绘制网络拓扑图
      this.serversTree.forEach(server => {
        this.paintServerNode(server, serverNodes, null);
      });
    },
    paintServerNode(server, serverNodes, masterServer) {
      let node = serverNodes[server.id];
      if (masterServer === null) {
        node.setLocation(
          2.5 * this.nodeWidth * (this.serversTree.indexOf(server) + 1) + 20,
          20
        );
        this.scene.add(node);
      } else {
        let masterNode = serverNodes[server.masterServerId];
        let y = masterNode.y + 2.5 * this.nodeHeight;
        let x =
          masterNode.x +
          (masterServer.slaves.indexOf(server) -
            masterServer.slaves.length / 2) *
            this.nodeWidth *
            2.5;
        node.setLocation(x, y);
        this.scene.add(node);
        let link = this.newLink(node, masterNode, "active");
        link.server = server;
        // 如果server的db_slave_status不是yes，那么与master服务器连接不同
        if (
          server.dbSlaveStatus != null &&
          server.dbSlaveStatus.slave_io_running.toLowerCase() != "yes"
        ) {
          link.dashedPattern = 5;
          link.strokeColor = "160,160,160";
          link.text = server.dbSlaveStatus.slave_io_running.toLowerCase();
        }
        link.click(e => {
          var targetServer = e.target.server;
          let content = `
            <p>服务器ID：${targetServer.id}</p>
            <p>服务器名称：${targetServer.serverName}</p>
            <p>从服务器与主服务器IO通信状态：${
              targetServer.dbSlaveStatus.slave_io_running
            }</p>`;
          content += `<p>服务器的slave_status:</p>`;
          content +=
            '<div style="overflow:auto;height:400px;"><ul style="margin-left:20px;">';
          for (let item in targetServer.dbSlaveStatus) {
            content += `<li>${item} : ${targetServer.dbSlaveStatus[item]}</li>`;
          }
          content += "</ul></div>";
          this.$alert(content, `${server.serverName} 的主从信息`, {
            dangerouslyUseHTMLString: true,
            confirmButtonText: "确定",
            callback: action => {}
          });
        });
        this.scene.add(link);
      }
      // 如果该server还有slave，继续递归
      if (server.slaves.length != 0) {
        server.slaves.forEach(s => {
          this.paintServerNode(s, serverNodes, server);
        });
      }
    },
    // 添加服务器的方法
    addServer() {
      // 清空以前的输入
      this.inputServer = {
        serverName: "",
        serverIp: "",
        serverPort: 3306,
        username: "",
        pass: ""
      };
      this.dialogVisible = true;
    },
    // 测试输入的数据库是否能连通
    testConn() {
      if (this.validateInput()) {
        // 测试该服务器是否能连通
        this.$axios
          .post(Api.testConn, this.inputServer)
          .then(result => {
            if (result.status) {
              this.$message({ type: "success", message: "连接成功" });
            } else {
              this.$message.error(result.msg);
            }
          })
          .catch(err => {
            this.$message.error(err.message);
          });
      } else {
        this.$alert("您需要正确输入所有的字段", "提示", {
          confirmButtonText: "确定",
          callback: action => {}
        });
      }
    },
    // 检查输入的服务器是否合法
    validateInput() {
      // 检测输入中所有的都不为空
      for (let item in this.inputServer) {
        let value = this.inputServer[item] + "";
        if (value == null || value.trim() == "") {
          return false;
        }
      }
      // 检测输入的端口是否符合要求
      let port = this.inputServer.serverPort;
      if (!(parseInt(port) + "" == port + "")) {
        return false;
      }
      return true;
    },
    addServerToBack() {
      if (this.validateInput()) {
        this.$axios
          .post(Api.addNewServer, this.inputServer)
          .then(result => {
            if (result.status) {
              this.$message({ type: "success", message: "添加成功" });
              this.dialogVisible = false;
              this.getServers();
            } else {
              this.$message.error(result.msg);
            }
          })
          .catch(err => {
            this.$message.error(err.message);
          });
          this.dialogVisible = false;
      } else {
        this.$alert("您需要正确输入所有的字段", "提示", {
          confirmButtonText: "确定",
          callback: action => {}
        });
      }
    },
    // 删除节点（selectedServer）
    deleteServer(){
      this.$axios.post(Api.deleteServer,{id:this.selectedServer.id}).then(result=>{
        if(result.status){
          this.$message({type:'success',message:'删除成功'})
          this.getServers();
        }else{
          this.$message.error(result.msg)
        }
      }).catch(err=>{
        this.$message.error(err.message)
      })
      this.menuVisible = false
    },
    // 关闭slave
    stopSlave(){
      this.$axios.post(Api.stopSlave,{id:this.selectedServer.id}).then(result=>{
        if(result.status){
          this.$message({type:"success",message:'关闭slave成功'})
          this.getServers();
        }else{
          this.$message.error(result.msg)
        }
      }).catch(err=>{
        this.$message.error(err.message)
      })
      this.menuVisible = false
    },
    // 开启slave
    startSlave(){
      this.$axios.post(Api.startSlave,{id:this.selectedServer.id}).then(result=>{
        if(result.status){
          this.$message({type:'success',message:'开启slave成功'})
          this.getServers();
        }else{
          this.$message.error(result.msg)
        }
      }).catch(err=>{
        this.$message.error(err.message)
      })
      this.menuVisible = false;
    },
    // 节点
    newNode: (x, y, w, h, text, scene) => {
      var node = new JTopo.Node(text);
      node.setLocation(x, y);
      node.setSize(w, h);
      node.text = text; // 文字
      node.textPosition = "Middle_Center"; // 文字居中
      node.font = "14px 微软雅黑"; // 字体
      node.fontColor = "255,255,255";
      scene.add(node);
      return node;
    },
    // 简单连线
    newLink: (nodeA, nodeZ, text, dashedPattern) => {
      var link = new JTopo.Link(nodeA, nodeZ, text);
      link.lineWidth = 3; // 线宽
      link.dashedPattern = dashedPattern; // 虚线
      link.bundleOffset = 60; // 折线拐角处的长度
      link.bundleGap = 20; // 线条之间的间隔
      link.textOffsetY = 3; // 文本偏移量（向下3个像素）
      link.strokeColor = "0,200,255";
      link.arrowsRadius = 15;
      return link;
    },
    // 折线
    newFoldLink: (nodeA, nodeZ, text, direction, dashedPattern, scene) => {
      var link = new JTopo.FoldLink(nodeA, nodeZ, text);
      link.direction = direction || "horizontal";
      link.arrowsRadius = 15; //箭头大小
      link.lineWidth = 3; // 线宽
      link.bundleOffset = 60; // 折线拐角处的长度
      link.bundleGap = 20; // 线条之间的间隔
      link.textOffsetY = 3; // 文本偏移量（向下3个像素）
      link.strokeColor = JTopo.util.randomColor(); // 线条颜色随机
      link.dashedPattern = dashedPattern;
      scene.add(link);
      return link;
    },
    // 二次折线
    newFlexionalLink: (nodeA, nodeZ, text, direction, dashedPattern, scene) => {
      var link = new JTopo.FlexionalLink(nodeA, nodeZ, text);
      link.direction = direction || "horizontal";
      link.arrowsRadius = 10;
      link.lineWidth = 3; // 线宽
      link.offsetGap = 35;
      link.bundleGap = 15; // 线条之间的间隔
      link.textOffsetY = 10; // 文本偏移量（向下15个像素）
      link.strokeColor = "0,250,0";
      link.dashedPattern = dashedPattern;
      scene.add(link);
      return link;
    },
    // 曲线
    newCurveLink: (nodeA, nodeZ, text, scene) => {
      var link = new JTopo.CurveLink(nodeA, nodeZ, text);
      link.lineWidth = 3; // 线宽
      scene.add(link);
      return link;
    }
  },
  watch: {
    // servers() {
    //   this.paintServers();
    // }
  },
  computed: {
    stage() {
      let canvas = this.$refs.canvas;
      return new JTopo.Stage(canvas);
    },
    scene() {
      let scene = new JTopo.Scene(this.stage);
      scene.backgroundColor = "0,0,0";
      return scene;
    }
  }
};
</script>
<style lang="less" scoped>
.main {
  padding-top: 20px;
  height: 100%;
  .ops {
    button {
      float: right;
      margin-right: 25px;
    }
  }
  .servers {
    height: 100%;
  }
  .menu{
    display: inline-block;
    position: fixed;
    width: 110px;
    left: 0;
    top: 0;
    border: 1px solid #ccc;
    background-color: #fff;
    div{
      padding: 0px;
    }
    li{
      list-style: none;
      padding: 5px;
      border-bottom: 1px solid #ccc;
      cursor: default;
      &:hover{
        background-color: #ddd;
      }
    }
  }
}
</style>
